#include "model.h"

Model::Model(QObject* parent) : QObject( parent )
{
   m_transformed = 0;
   m_hiddenSurfaceRemovalEnabled = true;
   m_posX = m_posY = m_posZ = 0;
   m_axisX = 1;
   m_axisY = m_axisZ = 0;
   m_angle = 0;
   m_scaleFactor = 1;
}

Model::Model( const char *filename, QObject *parent ) : QObject( parent )
{
   m_transformed = 0;
   load( filename );
}

void Model::print()
{
    cout << "Vertices:" << endl;
    for ( uint i = 0; i < m_vx.size(); i++)
    {
        cout << m_vx[i].pos.x;
        cout << " " << m_vx[i].pos.y;
        cout << " " << m_vx[i].pos.z << endl;
    }
    cout << endl << "Polygons:" << endl;
    for ( uint i = 0; i < m_poly.size(); i++ )
    {
       cout << "i = " << i;
        for ( uint j = 0; j < m_poly[i].poly.size(); j++)
        {
            cout<< "\t j = " << j << " " << m_poly[i].poly[j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}


int Model::load( const char *filename )
{
    if( !filename ) return -1;
    if( !strcmp( filename+strlen( filename )-3, "obj" ) ) loadObj( filename );
    delete m_transformed; m_transformed = 0;
    m_transformed = new RendererVertex[m_vx.size()];
    return 0;
}

int Model::loadObj( const char *filename )
{
    if( !filename ) return -1;
    fstream file( filename, ios::in );
    if( !file.is_open() ) return -2;

   m_poly.clear();
   m_vx.clear();
   m_tex.clear();
   m_normal.clear();
    string buf;
    Vertex3D tvx;
    poly tpoly;
    tex ttex;
    normal tnormal;

    while( !file.eof() )
    {
        tpoly.poly.clear();
	tpoly.tex.clear();
	tpoly.normal.clear();
        getline( file, buf );
        switch( buf[0] )
        {
            case 'v':
            switch ( buf[1] )
            {
                case ' ':
                    buf.erase( 0, 2 );
                    tvx.pos.x=atof( buf.substr( 0, buf.find_first_of( ' ' ) ).c_str() );
                    buf.erase( 0, buf.find_first_of( ' ' )+1 );
                    tvx.pos.y=atof( buf.substr( 0, buf.find_first_of( ' ' ) ).c_str() );
                    buf.erase( 0, buf.find_first_of( ' ' )+1 );
                    tvx.pos.z=atof( buf.c_str() );
                    tvx.r=1;
                    tvx.g=1;
                    tvx.b=1;
                    tvx.tex.x=0;
                    tvx.tex.y=0;
                    tvx.normal.x=0;
                    tvx.normal.y=1;
                    tvx.normal.z=0;
                    m_vx.push_back( tvx );
                    break;

                case 't':
                    buf.erase(0, 3);
                    ttex.x = atof( buf.substr(0, buf.find_first_of( ' ' )).c_str() );
                    buf.erase( 0, buf.find_first_of( ' ' )+1 );
                    ttex.y = atof( buf.c_str() );
                    m_tex.push_back( ttex );
                    break;

                case 'n':
                    buf.erase( 0, 3 );
                    tnormal.x = atof( buf.substr( 0, buf.find_first_of( ' ' ) ).c_str() );
                    buf.erase( 0, buf.find_first_of( ' ' )+1 );
                    tnormal.y = atof( buf.substr( 0, buf.find_first_of( ' ' ) ).c_str() );
                    buf.erase( 0, buf.find_first_of( ' ' )+1 );
                    tnormal.z=atof( buf.c_str() );
                    m_normal.push_back( tnormal );
                    break;

                default:
                    break;
            }
            break;

            case 'f':
                int tmpint;
                buf.erase( 0, 2 );
                string::size_type l;
                string::size_type k;
		string::size_type detect;
		l = buf.find_first_of( ' ' );
		detect = buf.find_first_of( '/' );
                do
                {
		   if( detect == string::npos )
		   {
		      // simple model
		      l = buf.find_first_of( ' ' );
		      tmpint = atoi( buf.substr( 0, l ).c_str() )-1;
		      tpoly.poly.push_back( tmpint );
		      buf.erase( 0,l+1 );
		   }
		   else
		   {
		      // complex model
		      // vertex
		      k = buf.find_first_of( '/' );
		      if( k == string::npos ) continue; // this shouldn't happen
		      tmpint = atoi( buf.substr( 0,k ).c_str() )-1;
		      tpoly.poly.push_back( tmpint );
		      buf.erase( 0, k+1 );
		      
		      // tex
		      k = buf.find_first_of( '/' );
		      if( k == string::npos ) continue; // this shouldn't happen
		      tmpint = atoi( buf.substr( 0,k ).c_str() )-1;
		      tpoly.tex.push_back( tmpint );
		      buf.erase( 0, k+1 );
		      
		      // normal
		      k = buf.find_first_of( ' ' );
		      if( k == string::npos ) k = buf.size();
		      tmpint = atoi( buf.substr( 0,k ).c_str() )-1;
		      tpoly.normal.push_back( tmpint );
		      buf.erase( 0, k+1 );
		   }
                }while( l!=string::npos && buf.size() > 0);
                m_poly.push_back( tpoly );
                break;

            default:
                break;
        }
    }

    file.close();
    return 0;
}


void Model::moveTo(float x, float y, float z)
{
   m_posX = x;
   m_posY = y;
   m_posZ = z;
}

void Model::move(float x, float y, float z)
{
   m_posX += x;
   m_posY += y;
   m_posZ += z;
}

void Model::rotate(float x, float y, float z, float angle)
{
   m_axisX = x;
   m_axisY = y;
   m_axisZ = z;
   m_angle = angle;
}

void Model::scale(float factor)
{
   m_scaleFactor = factor;
}


bool Model::hiddenSurfaceRemovalEnabled()
{
    return m_hiddenSurfaceRemovalEnabled;
}

void Model::setHiddenSurfaceRemovalEnabled( bool enable )
{
    m_hiddenSurfaceRemovalEnabled = enable;
}

bool Model::checkVisibility(RendererVertex v1, RendererVertex v2, RendererVertex v3)
{
    float area = 0;
    area = ( (v2.pos.x - v1.pos.x) * (v2.pos.y + v1.pos.y) ) +
           ( (v3.pos.x - v2.pos.x) * (v3.pos.y + v2.pos.y) ) +
           ( (v1.pos.x - v3.pos.x) * (v1.pos.y + v3.pos.y) );
    if( area < 0) return false;
    else return true;
}

void Model::render(Renderer *renderer)
{
   // movement and rotation
   renderer->scale( m_scaleFactor );
   renderer->translate( m_posX, m_posY, m_posZ );
   renderer->rotate( m_axisX, m_axisY, m_axisZ, m_angle );
   
   // calculations and render
//     renderer->transform(m_vx, m_transformed);

    int v1, v2, v3;
    int t1, t2, t3;
    int n1, n2, n3;
    vector<Vertex3D> tmpVx;
    
    for (uint i = 0; i < m_poly.size(); i++)
    {
	 tmpVx.clear();

        v1 = m_poly[i].poly[0];
        v2 = m_poly[i].poly[1];
        v3 = m_poly[i].poly[2];
	
	if( m_poly[i].tex.size() > 0 )
        {
            t1 = m_poly[i].tex[0];
            t2 = m_poly[i].tex[1];
            t3 = m_poly[i].tex[2];
	
	    m_vx[v1].tex.x = m_tex[t1].x;
            m_vx[v1].tex.y = m_tex[t1].y;
            m_vx[v2].tex.x = m_tex[t2].x;
            m_vx[v2].tex.y = m_tex[t2].y;
            m_vx[v3].tex.x = m_tex[t3].x;
            m_vx[v3].tex.y = m_tex[t3].y;
        }

        if( m_poly[i].normal.size() > 0 )
        {
            n1 = m_poly[i].normal[0];
            n2 = m_poly[i].normal[1];
            n3 = m_poly[i].normal[2];
	    
	    m_vx[v1].normal.x = m_normal[n1].x;
            m_vx[v1].normal.y = m_normal[n1].y;
            m_vx[v1].normal.z = m_normal[n1].z;
            m_vx[v2].normal.x = m_normal[n2].x;
            m_vx[v2].normal.y = m_normal[n2].y;
            m_vx[v2].normal.z = m_normal[n2].z;
            m_vx[v3].normal.x = m_normal[n3].x;
            m_vx[v3].normal.y = m_normal[n3].y;
            m_vx[v3].normal.z = m_normal[n3].z;
        }

        tmpVx.push_back( m_vx[v1] );
        tmpVx.push_back( m_vx[v2] );
        tmpVx.push_back( m_vx[v3] );
        renderer->transform(tmpVx, m_transformed);


        if(m_hiddenSurfaceRemovalEnabled == false || checkVisibility( m_transformed[0], m_transformed[1], m_transformed[2] ) )
        {
            if (m_transformed[0].pos.z > 0 ||
                m_transformed[1].pos.z > 0 ||
                m_transformed[2].pos.z > 0) continue; // ne crtati poligone koji su iza kamere.
            renderer->render(m_transformed[0], m_transformed[1], m_transformed[2]);
        }
    }
}


Model::~Model()
{
    m_vx.clear();
    m_poly.clear();
    delete [] m_transformed;
}
